package org.gaaidou.ptarmigan.server.protocol;

import org.gaaidou.ptarmigan.server.util.ByteArray;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class HttpParser {

    private static final int MAX_BUFF_SIZE = 64;

    public static HttpRequest parseRequest(InputStream is) throws IOException {
        return new HttpRequest(readMethod(is), readPath(is), readVersion(is), readHeader(is), readBody(is));
    }

    public static void writeResponse(HttpResponse response, OutputStream os) throws IOException {
        os.write(String.format("%s %s %s\n", null != response.getVersion() ? response.getVersion() : "HTTP/1.1", response.getStatusCode(), response.getReasonPhrase()).getBytes(StandardCharsets.UTF_8));
        if (null != response.getHeader() && !response.getHeader().isEmpty()) {
            for (Map.Entry<String, String> h : response.getHeader().entrySet()) {
                os.write(String.format("%s: %s\n", h.getKey(), h.getValue()).getBytes(StandardCharsets.UTF_8));
            }
        }

        int contentLength = null != response.getBody() ? response.getBody().length() : 0;
        os.write(String.format("Content-Length: %s\n", contentLength).getBytes(StandardCharsets.UTF_8));
        os.write("\n".getBytes(StandardCharsets.UTF_8));

        if (contentLength > 0) {
            os.write(response.getBody().getBytes(StandardCharsets.UTF_8));
        }
        os.flush();
    }

    private static String readMethod(InputStream is) throws IOException {
        int read;
        ByteArray buff = new ByteArray(5);
        while ((read = is.read()) != ' ') {
            buff.add((byte) read);
        }
        return new String(buff.compressAndGet());
    }

    private static String readPath(InputStream is) throws IOException {
        int read;
        ByteArray buff = new ByteArray(1024);
        while ((read = is.read()) != ' ') {
            buff.add((byte) read);
        }
        return new String(buff.compressAndGet()).trim();
    }

    private static String readVersion(InputStream is) throws IOException {
        int read;
        ByteArray buff = new ByteArray(8);
        while ((read = is.read()) != '\n') {
            buff.add((byte) read);
        }
        return new String(buff.compressAndGet()).trim();
    }

    private static Map<String, String> readHeader(InputStream is) throws IOException {
        int read;

        Map<String, String> result = new HashMap<>();
        while (true) {
            ByteArray buff = new ByteArray(1024);
            while ((read = is.read()) != ':') {
                if (read == '\r') {
                    return result;
                }
                buff.add((byte) read);
            }
            String key = new String(buff.compressAndGet()).trim();

            buff = new ByteArray(1024);
            while ((read = is.read()) != '\n') {
                buff.add((byte) read);
            }
            String value = new String(buff.compressAndGet()).trim();
            result.put(key, value);
        }


    }

    private static String readBody(InputStream is) throws IOException {
        StringBuilder body = new StringBuilder();
        int available = is.available();
        for (int index = 0; index < available / MAX_BUFF_SIZE + 1; index++) {
            byte[] buff = new byte[MAX_BUFF_SIZE];
            if (-1 == is.read(buff)) {
                break;
            }
            body.append(new String(buff));
        }
        return body.toString().trim();
    }
}
